import zipfile
from reportlab.lib.pagesizes import letter, A4
from reportlab.platypus import SimpleDocTemplate, Paragraph, Spacer, Image, Table, TableStyle
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch
from reportlab.lib import colors
from reportlab.pdfgen import canvas
from reportlab.lib.utils import ImageReader
import io

from flask import Flask, request, render_template, jsonify, send_file
from flask_cors import CORS
import akshare as ak
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from sklearn.preprocessing import MinMaxScaler
from sympy.polys.rootisolation import A4
from tensorflow.keras.models import Sequential
from tensorflow.keras.layers import LSTM, Dense, Dropout
import io, base64, datetime, warnings
warnings.filterwarnings("ignore")
import matplotlib
matplotlib.use('Agg')  # 非交互式后端
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']  # 中文字体
plt.rcParams['axes.unicode_minus'] = False  # 解决负号显示问题


app = Flask(__name__)
CORS(app)

# ---------------- 获取A股数据 ----------------
def fetch_data(code: str, start_date="20150101"):
    df = ak.stock_zh_a_hist(symbol=code, period="daily", start_date=start_date, adjust="qfq")
    if df is None or df.empty:
        raise ValueError("未获取到数据，请检查股票代码或网络。")
    df['日期'] = pd.to_datetime(df['日期'])
    df['收盘'] = pd.to_numeric(df['收盘'], errors='coerce')
    df = df[['日期', '收盘']].dropna().sort_values('日期').reset_index(drop=True)
    return df

# ---------------- 构建LSTM模型 ----------------
def build_lstm_model(X_train, y_train):
    model = Sequential([
        LSTM(64, return_sequences=True, input_shape=(X_train.shape[1], 1)),
        Dropout(0.2),
        LSTM(64, return_sequences=False),
        Dropout(0.2),
        Dense(32, activation='relu'),
        Dense(1)
    ])
    model.compile(optimizer='adam', loss='mse')
    model.fit(X_train, y_train, epochs=20, batch_size=32, verbose=0)
    return model

# ---------------- LSTM预测逻辑 ----------------
def lstm_predict(df, future_days=90):
    data = df[['收盘']].values
    scaler = MinMaxScaler((0, 1))
    scaled_data = scaler.fit_transform(data)

    seq_len = 60
    X, y = [], []
    for i in range(seq_len, len(scaled_data)):
        X.append(scaled_data[i - seq_len:i, 0])
        y.append(scaled_data[i, 0])
    X, y = np.array(X), np.array(y)
    X = np.reshape(X, (X.shape[0], X.shape[1], 1))

    model = build_lstm_model(X, y)

    last_seq = scaled_data[-seq_len:]
    predictions = []
    for _ in range(future_days):
        pred = model.predict(last_seq.reshape(1, seq_len, 1), verbose=0)
        predictions.append(pred[0][0])
        last_seq = np.append(last_seq[1:], pred).reshape(seq_len, 1)

    predicted_prices = scaler.inverse_transform(np.array(predictions).reshape(-1, 1)).flatten()
    future_dates = pd.date_range(df['日期'].iloc[-1] + pd.Timedelta(days=1), periods=future_days)
    return predicted_prices, future_dates, scaler.inverse_transform(scaled_data).flatten(), model

# ---------------- 绘图 ----------------
def build_plot(df, predicted_prices, future_dates):
    fig, ax = plt.subplots(figsize=(12, 6))
    ax.plot(df['日期'], df['收盘'], label="历史收盘价", linewidth=1.2)
    ax.plot(future_dates, predicted_prices, label="预测价格（未来90天）", linestyle='--', color='orange')
    ax.set_title("LSTM预测：历史收盘价 + 未来90天预测")
    ax.set_xlabel("日期")
    ax.set_ylabel("价格")
    ax.legend()
    ax.grid(True, linestyle='--', alpha=0.4)

    buf = io.BytesIO()
    plt.tight_layout()
    plt.savefig(buf, format="png", dpi=150)
    plt.close(fig)
    buf.seek(0)
    return base64.b64encode(buf.read()).decode('utf-8')

# ---------------- 生成文本分析 ----------------
def build_summary(df, predicted_prices, future_dates):
    last_close = df['收盘'].iloc[-1]
    final_pred = predicted_prices[-1]
    pct_change = (final_pred - last_close) / last_close * 100
    trend = "上涨趋势" if pct_change > 3 else "下跌趋势" if pct_change < -3 else "横盘震荡"
    return (
        f"最后交易日收盘价：{last_close:.2f}\n"
        f"预测 {len(future_dates)} 天后的价格：{final_pred:.2f}\n"
        f"涨跌幅：{pct_change:.2f}%\n"
        f"趋势判断：{trend}\n"
        f"（基于 LSTM 深度学习预测，仅供参考，不构成投资建议）"
    )


# ---------------- 生成PDF报告 ----------------
def generate_pdf_report(code, df, predicted_prices, future_dates):
    # 创建PDF缓冲区
    pdf_buffer = io.BytesIO()

    # 创建PDF文档
    doc = SimpleDocTemplate(pdf_buffer, pagesize=A4, topMargin=0.5 * inch)
    styles = getSampleStyleSheet()

    # 创建自定义样式
    title_style = ParagraphStyle(
        'CustomTitle',
        parent=styles['Heading1'],
        fontSize=18,
        textColor=colors.HexColor('#1a3b5d'),
        spaceAfter=30,
        alignment=1  # 居中
    )

    heading_style = ParagraphStyle(
        'CustomHeading',
        parent=styles['Heading2'],
        fontSize=14,
        textColor=colors.HexColor('#2c5530'),
        spaceAfter=12
    )

    normal_style = ParagraphStyle(
        'CustomNormal',
        parent=styles['Normal'],
        fontSize=10,
        textColor=colors.HexColor('#333333'),
        spaceAfter=6
    )
'''
    # 构建PDF内容
    story = []

    # 标题
    story.append(Paragraph(f"股票代码 {code} 分析报告", title_style))
    story.append(Spacer(1, 0.2 * inch))

    # 生成时间
    current_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
    story.append(Paragraph(f"报告生成时间: {current_time}", normal_style))
    story.append(Spacer(1, 0.2 * inch))

    # 生成图表并添加到PDF
    fig, ax = plt.subplots(figsize=(10, 6))
    ax.plot(df['日期'], df['收盘'], label="历史收盘价", linewidth=1.5)
    ax.plot(future_dates, predicted_prices, label="预测价格（未来90天）", linestyle='--', color='red')
    ax.set_title(f"股票{code}价格预测", fontsize=14)
    ax.set_xlabel("日期")
    ax.set_ylabel("价格（元）")
    ax.legend()
    ax.grid(True, linestyle='--', alpha=0.7)

    # 保存图表到缓冲区
    chart_buffer = io.BytesIO()
    plt.tight_layout()
    plt.savefig(chart_buffer, format='png', dpi=150, bbox_inches='tight')
    plt.close(fig)
    chart_buffer.seek(0)

    # 添加图表到PDF
    chart_image = Image(chart_buffer, width=6 * inch, height=3.5 * inch)
    story.append(chart_image)
    story.append(Spacer(1, 0.3 * inch))

    # 关键数据统计
    story.append(Paragraph("关键数据统计", heading_style))

    last_close = df['收盘'].iloc[-1]
    final_pred = predicted_prices[-1]
    pct_change = (final_pred - last_close) / last_close * 100
    trend = "上涨趋势" if pct_change > 3 else "下跌趋势" if pct_change < -3 else "横盘震荡"

    # 创建数据表格
    data = [
        ['指标', '数值'],
        ['最后交易日收盘价', f'{last_close:.2f} 元'],
        ['预测90天后价格', f'{final_pred:.2f} 元'],
        ['预测涨跌幅', f'{pct_change:+.2f}%'],
        ['趋势判断', trend],
        ['历史数据起始日期', df['日期'].iloc[0].strftime('%Y-%m-%d')],
        ['历史数据结束日期', df['日期'].iloc[-1].strftime('%Y-%m-%d')],
        ['数据总数', f'{len(df)} 条'],
        ['历史最高价', f'{df["收盘"].max():.2f} 元'],
        ['历史最低价', f'{df["收盘"].min():.2f} 元']
    ]

    table = Table(data, colWidths=[2.5 * inch, 2.5 * inch])
    table.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#f0f0f0')),
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.HexColor('#333333')),
        ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
        ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
        ('FONTSIZE', (0, 0), (-1, 0), 12),
        ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
        ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor('#f9f9f9')),
        ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
        ('FONTSIZE', (0, 1), (-1, -1), 10),
        ('GRID', (0, 0), (-1, -1), 1, colors.HexColor('#dddddd'))
    ]))

    story.append(table)
    story.append(Spacer(1, 0.3 * inch))

    # 预测数据统计
    story.append(Paragraph("预测数据统计", heading_style))

    pred_data = [
        ['统计指标', '数值'],
        ['预测起始日期', future_dates[0].strftime('%Y-%m-%d')],
        ['预测结束日期', future_dates[-1].strftime('%Y-%m-%d')],
        ['预测最高价', f'{predicted_prices.max():.2f} 元'],
        ['预测最低价', f'{predicted_prices.min():.2f} 元'],
        ['预测平均价', f'{predicted_prices.mean():.2f} 元']
    ]

    pred_table = Table(pred_data, colWidths=[2.5 * inch, 2.5 * inch])
    pred_table.setStyle(TableStyle([
        ('BACKGROUND', (0, 0), (-1, 0), colors.HexColor('#e8f4f8')),
        ('TEXTCOLOR', (0, 0), (-1, 0), colors.HexColor('#2c3e50')),
        ('ALIGN', (0, 0), (-1, -1), 'CENTER'),
        ('FONTNAME', (0, 0), (-1, 0), 'Helvetica-Bold'),
        ('FONTSIZE', (0, 0), (-1, 0), 12),
        ('BOTTOMPADDING', (0, 0), (-1, 0), 12),
        ('BACKGROUND', (0, 1), (-1, -1), colors.HexColor('#f0f8ff')),
        ('FONTNAME', (0, 1), (-1, -1), 'Helvetica'),
        ('FONTSIZE', (0, 1), (-1, -1), 10),
        ('GRID', (0, 0), (-1, -1), 1, colors.HexColor('#cce7ff'))
    ]))

    story.append(pred_table)
    story.append(Spacer(1, 0.3 * inch))

    # 风险提示
    story.append(Paragraph("风险提示", heading_style))
    risk_text = """
本报告基于LSTM深度学习算法生成，分析结果仅供参考，不构成任何投资建议。股市有风险，投资需谨慎。
预测结果基于历史数据，实际市场表现可能受多种因素影响，包括但不限于宏观经济、政策变化、公司基本面等。
投资者应结合自身风险承受能力做出投资决策。
"""
    story.append(Paragraph(risk_text, normal_style))

    # 生成PDF
    doc.build(story)
    pdf_buffer.seek(0)

    return pdf_buffer
'''

# ---------------- Flask 路由 ----------------
# 在 Flask 路由部分添加新的下载路由
@app.route("/download")
def download_report():
    code = request.args.get("code", "600519").strip()
    try:
        df = fetch_data(code)
        predicted_prices, future_dates, _, _ = lstm_predict(df, 90)

        # 生成图表
        fig, ax = plt.subplots(figsize=(12, 6))
        ax.plot(df['日期'], df['收盘'], label="历史收盘价", linewidth=1.2)
        ax.plot(future_dates, predicted_prices, label="预测价格（未来90天）", linestyle='--', color='orange')
        ax.set_title(f"股票{code} LSTM预测：历史收盘价 + 未来90天预测")
        ax.set_xlabel("日期")
        ax.set_ylabel("价格")
        ax.legend()
        ax.grid(True, linestyle='--', alpha=0.4)

        # 保存图表到BytesIO
        chart_buf = io.BytesIO()
        plt.tight_layout()
        plt.savefig(chart_buf, format="png", dpi=150)
        plt.close(fig)
        chart_buf.seek(0)

        # 生成分析报告文本
        last_close = df['收盘'].iloc[-1]
        final_pred = predicted_prices[-1]
        pct_change = (final_pred - last_close) / last_close * 100
        trend = "上涨趋势" if pct_change > 3 else "下跌趋势" if pct_change < -3 else "横盘震荡"

        report_content = f"""
股票代码: {code}
分析报告生成时间: {datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}

最后交易日收盘价: {last_close:.2f}
预测 {len(future_dates)} 天后的价格: {final_pred:.2f}
涨跌幅: {pct_change:.2f}%
趋势判断: {trend}

历史数据统计:
- 数据起始日期: {df['日期'].iloc[0].strftime('%Y-%m-%d')}
- 数据结束日期: {df['日期'].iloc[-1].strftime('%Y-%m-%d')}
- 数据总数: {len(df)} 条
- 最高收盘价: {df['收盘'].max():.2f}
- 最低收盘价: {df['收盘'].min():.2f}
- 平均收盘价: {df['收盘'].mean():.2f}

预测数据统计:
- 预测起始日期: {future_dates[0].strftime('%Y-%m-%d')}
- 预测结束日期: {future_dates[-1].strftime('%Y-%m-%d')}
- 预测最高价: {predicted_prices.max():.2f}
- 预测最低价: {predicted_prices.min():.2f}
- 预测平均价: {predicted_prices.mean():.2f}

风险提示:
本报告基于LSTM深度学习算法生成，仅供参考，不构成投资建议。
股市有风险，投资需谨慎。
        """

        # 创建ZIP文件包含图表和报告
        zip_buf = io.BytesIO()
        with zipfile.ZipFile(zip_buf, 'w', zipfile.ZIP_DEFLATED) as zip_file:
            # 添加图表
            zip_file.writestr(f'stock_{code}_chart.png', chart_buf.getvalue())
            # 添加报告
            zip_file.writestr(f'stock_{code}_report.txt', report_content.encode('utf-8'))

        zip_buf.seek(0)

        # 返回ZIP文件下载
        filename = f"stock_{code}_analysis_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.zip"
        return send_file(
            zip_buf,
            mimetype='application/zip',
            as_attachment=True,
            download_name=filename
        )

    except Exception as e:
        return jsonify({"status": "error", "message": str(e)}), 400
'''
@app.route("/download")
def download_report():
    code = request.args.get("code", "600519").strip()
    try:
        df = fetch_data(code)
        predicted_prices, future_dates, _, _ = lstm_predict(df, 90)

        # 生成PDF报告
        pdf_buffer = generate_pdf_report(code, df, predicted_prices, future_dates)

        # 返回PDF文件下载
        filename = f"stock_{code}_analysis_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.pdf"

        return send_file(
            pdf_buffer,
            mimetype='application/pdf',
            as_attachment=True,
            download_name=filename
        )

    except Exception as e:
        return jsonify({"status": "error", "message": str(e)}), 400
'''
@app.route("/")
def index():
    return render_template("index.html", code="600519", img=None, summary=None, error=None)

@app.route("/predict")
def predict_page():
    code = request.args.get("code", "600519").strip()
    try:
        df = fetch_data(code)
        predicted_prices, future_dates, _, _ = lstm_predict(df, 90)
        img = build_plot(df, predicted_prices, future_dates)
        summary = build_summary(df, predicted_prices, future_dates)
        return render_template("index.html", code=code, img=img, summary=summary, error=None)
    except Exception as e:
        return render_template("index.html", code=code, img=None, summary=None, error=str(e))

@app.route("/api/predict")
def api_predict():
    code = request.args.get("code", "600519").strip()
    try:
        df = fetch_data(code)
        predicted_prices, future_dates, _, _ = lstm_predict(df, 90)
        img = build_plot(df, predicted_prices, future_dates)
        summary = build_summary(df, predicted_prices, future_dates)
        return jsonify({
            "status": "ok",
            "code": code,
            "summary": summary,
            "image_base64": img
        })
    except Exception as e:
        return jsonify({"status": "error", "message": str(e)}), 400

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000, debug=True)
